package com.qumitech.visualnovel.component.util;

import com.qumitech.visualnovel.component.annotation.ExcelAnnotation;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * ExcelUtils 工具类
 * @param <T> 泛型类型
 *
 * @author anonymous
 */
public class ExcelUtils<T> {
    /**
     * 从excel导入,返回一个list集合
     * 导入的excel文件
     */
    Class<T> clazz;

    public ExcelUtils(Class<T> clazz) {
        this.clazz = clazz;
    }

    public Collection<T> importExcelX(InputStream is, String... pattern) {
        Collection<T> dist = new ArrayList<T>();
        try {
            /**
             * 类反射得到调用方法
             */
            // 得到目标目标类的所有的字段列表
            Field[] fields = clazz.getDeclaredFields();
            // 将所有标有Annotation的字段，也就是允许导入数据的字段,放入到一个map中
            Map<String, Method> fieldMap = new HashMap<>(16);
            // 循环读取所有字段
            for (Field field : fields) {
                // 得到单个字段上的Annotation
                ExcelAnnotation excelAnnotation = field.getAnnotation(ExcelAnnotation.class);
                // 如果标识了Annotationd
                if (excelAnnotation != null) {
                    String fieldName = field.getName();
                    // 构造设置了Annotation的字段的Setter方法
                    String setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    // 构造调用的method
                    Method setMethod = clazz.getMethod(setMethodName, new Class[]{field.getType()});
                    // 将这个method以Annotaion的名字为key来存入
                    fieldMap.put(excelAnnotation.value(), setMethod);
                }
            }

            /**
             * excel的解析开始
             */
            // 将传入的File构造为FileInputStream;
            //FileInputStream inputStream = new FileInputStream(file);
            // 得到工作表
            XSSFWorkbook book = new XSSFWorkbook(is);
            // 得到第一页
            XSSFSheet sheet = book.getSheetAt(0);
            // 得到第一面的所有行
            Iterator<Row> row = sheet.rowIterator();

            /**
             * 标题解析
             */
            // 得到第一行，也就是标题行
            Row titleRow = row.next();
            // 得到第一行的所有列
            Iterator<Cell> cellTitle = titleRow.cellIterator();
            // 将标题的文字内容放入到一个map中
            Map<Integer, String> titleMap = new HashMap<Integer, String>();
            // 从标题第一列开始
            int i = 0;
            // 循环标题所有的列
            while (cellTitle.hasNext()) {
                Cell cell = (Cell) cellTitle.next();
                String value = cell.getStringCellValue();
                titleMap.put(i, value);
                i++;
            }

            /**
             * 解析内容行
             */
            while (row.hasNext()) {
                // 标题下的第一行
                Row rown = row.next();
                // 行的所有列
                Iterator<Cell> cellBody = rown.cellIterator();
                // 得到传入类的实例
                T tObject = clazz.newInstance();
                // 遍历一行的列
                int col = 0;
                while (cellBody.hasNext()) {
                    Cell cell = (Cell) cellBody.next();
                    // 这里得到此列的对应的标题
                    String titleString = titleMap.get(col++);
                    // 如果这一列的标题和类中的某一列的Annotation相同，那么则调用此类的的set方法，进行设值
                    if (fieldMap.containsKey(titleString)) {
                        Method setMethod = fieldMap.get(titleString);
                        //得到setter方法的参数
                        Type[] types = setMethod.getGenericParameterTypes();
                        //只要一个参数
                        String xclass = String.valueOf(types[0]);
                        //判断参数类型
                        if ("class java.lang.String".equals(xclass)) {
                            cell.setCellType(Cell.CELL_TYPE_STRING);
                            setMethod.invoke(tObject, cell.getStringCellValue());
                        } else if ("class java.util.Date".equals(xclass)) {
                            setMethod.invoke(tObject, cell.getDateCellValue());
                        } else if ("class java.lang.Boolean".equals(xclass)) {
                            Boolean boolName = true;
                            if ("否".equals(cell.getStringCellValue())) {
                                boolName = false;
                            }
                            setMethod.invoke(tObject, boolName);
                        } else if ("class java.lang.Integer".equals(xclass)) {
                            setMethod.invoke(tObject, new Integer(String.valueOf((int) cell.getNumericCellValue())));
                        } else if ("class java.lang.Long".equals(xclass)) {
                            setMethod.invoke(tObject, new Long(cell.getStringCellValue()));
                        } else {
                            //
                        }
                    }
                }
                dist.add(tObject);
            }

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            return null;
        }
        return dist;
    }

    public Collection<T> importExcel(InputStream is, String... pattern) {
        Collection<T> dist = new ArrayList<T>();
        try {
            /**
             * 类反射得到调用方法
             */
            // 得到目标目标类的所有的字段列表
            Field[] fields = clazz.getDeclaredFields();
            // 将所有标有Annotation的字段，也就是允许导入数据的字段,放入到一个map中
            Map<String, Method> fieldMap = new HashMap<String, Method>();
            // 循环读取所有字段
            for (Field field : fields) {
                // 得到单个字段上的Annotation
                ExcelAnnotation excelAnnotation = field.getAnnotation(ExcelAnnotation.class);
                // 如果标识了Annotationd
                if (excelAnnotation != null) {
                    String fieldName = field.getName();
                    // 构造设置了Annotation的字段的Setter方法
                    String setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    // 构造调用的method
                    Method setMethod = clazz.getMethod(setMethodName, new Class[]{field.getType()});
                    // 将这个method以Annotaion的名字为key来存入
                    fieldMap.put(excelAnnotation.value(), setMethod);
                }
            }

            /**
             * excel的解析开始
             */
            // 将传入的File构造为FileInputStream;
            //FileInputStream inputStream = new FileInputStream(file);
            // 得到工作表
            HSSFWorkbook book = new HSSFWorkbook(is);
            // 得到第一页
            HSSFSheet sheet = book.getSheetAt(0);
            // 得到第一面的所有行
            Iterator<Row> row = sheet.rowIterator();

            /**
             * 标题解析
             */
            // 得到第一行，也就是标题行
            Row titleRow = row.next();
            // 得到第一行的所有列
            Iterator<Cell> cellTitle = titleRow.cellIterator();
            // 将标题的文字内容放入到一个map中
            Map<Integer, String> titleMap = new HashMap<Integer, String>();
            // 从标题第一列开始
            int i = 0;
            // 循环标题所有的列
            while (cellTitle.hasNext()) {
                Cell cell = (Cell) cellTitle.next();
                String value = cell.getStringCellValue();
                titleMap.put(i, value);
                i++;
            }

            /**
             * 解析内容行
             */
            while (row.hasNext()) {
                // 标题下的第一行
                Row rown = row.next();
                // 行的所有列
                Iterator<Cell> cellBody = rown.cellIterator();
                // 得到传入类的实例
                T tObject = clazz.newInstance();
                // 遍历一行的列
                int col = 0;
                while (cellBody.hasNext()) {
                    Cell cell = (Cell) cellBody.next();
                    // 这里得到此列的对应的标题
                    String titleString = titleMap.get(col++);
                    // 如果这一列的标题和类中的某一列的Annotation相同，那么则调用此类的的set方法，进行设值
                    if (fieldMap.containsKey(titleString)) {
                        Method setMethod = fieldMap.get(titleString);
                        //得到setter方法的参数
                        Type[] types = setMethod.getGenericParameterTypes();
                        //只要一个参数
                        String xclass = String.valueOf(types[0]);
                        //判断参数类型
                        if ("class java.lang.String".equals(xclass)) {
                            setMethod.invoke(tObject, cell.getStringCellValue());
                        } else if ("class java.util.Date".equals(xclass)) {
                            setMethod.invoke(tObject, cell.getDateCellValue());
                        } else if ("class java.lang.Boolean".equals(xclass)) {
                            Boolean boolName = true;
                            if ("否".equals(cell.getStringCellValue())) {
                                boolName = false;
                            }
                            setMethod.invoke(tObject, boolName);
                        } else if ("class java.lang.Integer".equals(xclass)) {
                            setMethod.invoke(tObject, new Integer(String.valueOf((int) cell.getNumericCellValue())));
                        } else if ("class java.lang.Long".equals(xclass)) {
                            setMethod.invoke(tObject, new Long(cell.getStringCellValue()));
                        } else {
                            //
                        }
                    }
                }
                dist.add(tObject);
            }

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            return null;
        }
        return dist;
    }

    public Collection<T> importExcel(File file, String... pattern) {
        Collection<T> dist = new ArrayList<T>();
        try {
            /**
             * 类反射得到调用方法
             */
            // 得到目标目标类的所有的字段列表
            Field[] fields = clazz.getDeclaredFields();
            // 将所有标有Annotation的字段，也就是允许导入数据的字段,放入到一个map中
            Map<String, Method> fieldMap = new HashMap<String, Method>();
            // 循环读取所有字段
            for (Field field : fields) {
                // 得到单个字段上的Annotation
                ExcelAnnotation excelAnnotation = field.getAnnotation(ExcelAnnotation.class);
                // 如果标识了Annotationd
                if (excelAnnotation != null) {
                    String fieldName = field.getName();
                    // 构造设置了Annotation的字段的Setter方法
                    String setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    // 构造调用的method
                    Method setMethod = clazz.getMethod(setMethodName, new Class[]{field.getType()});
                    // 将这个method以Annotaion的名字为key来存入
                    fieldMap.put(excelAnnotation.value(), setMethod);
                }
            }

            /**
             * excel的解析开始
             */
            // 将传入的File构造为FileInputStream;
            FileInputStream inputStream = new FileInputStream(file);
            // 得到工作表
            HSSFWorkbook book = new HSSFWorkbook(inputStream);
            // 得到第一页
            HSSFSheet sheet = book.getSheetAt(0);
            // 得到第一面的所有行
            Iterator<Row> row = sheet.rowIterator();

            /**
             * 标题解析
             */
            // 得到第一行，也就是标题行
            Row titleRow = row.next();
            // 得到第一行的所有列
            Iterator<Cell> cellTitle = titleRow.cellIterator();
            // 将标题的文字内容放入到一个map中
            Map<Integer, String> titleMap = new HashMap<Integer, String>();
            // 从标题第一列开始
            int i = 0;
            // 循环标题所有的列
            while (cellTitle.hasNext()) {
                Cell cell = (Cell) cellTitle.next();
                String value = cell.getStringCellValue();
                titleMap.put(i, value);
                i++;
            }

            /**
             * 解析内容行
             */
            while (row.hasNext()) {
                // 标题下的第一行
                Row rown = row.next();
                // 行的所有列
                Iterator<Cell> cellBody = rown.cellIterator();
                // 得到传入类的实例
                T tObject = clazz.newInstance();
                // 遍历一行的列
                int col = 0;
                while (cellBody.hasNext()) {
                    Cell cell = (Cell) cellBody.next();
                    // 这里得到此列的对应的标题
                    String titleString = titleMap.get(col++);
                    // 如果这一列的标题和类中的某一列的Annotation相同，那么则调用此类的的set方法，进行设值
                    if (fieldMap.containsKey(titleString)) {
                        Method setMethod = fieldMap.get(titleString);
                        //得到setter方法的参数
                        Type[] types = setMethod.getGenericParameterTypes();
                        //只要一个参数
                        String xclass = String.valueOf(types[0]);
                        //判断参数类型
                        if ("class java.lang.String".equals(xclass)) {
                            setMethod.invoke(tObject, cell.getStringCellValue());
                        } else if ("class java.util.Date".equals(xclass)) {
                            setMethod.invoke(tObject, cell.getDateCellValue());
                        } else if ("class java.lang.Boolean".equals(xclass)) {
                            Boolean boolName = true;
                            if ("否".equals(cell.getStringCellValue())) {
                                boolName = false;
                            }
                            setMethod.invoke(tObject, boolName);
                        } else if ("class java.lang.Integer".equals(xclass)) {
                            setMethod.invoke(tObject, new Integer(String.valueOf((int) cell.getNumericCellValue())));
                        } else if ("class java.lang.Long".equals(xclass)) {
                            setMethod.invoke(tObject, new Long(cell.getStringCellValue()));
                        } else {
                            //
                        }
                    }
                }
                dist.add(tObject);
            }

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            return null;
        }
        return dist;
    }

    /**
     * 格式化日期
     */
	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 导出到excel中去
     *
     * @param title    excel的工作表名
     * @param dataset  导出的数据集合
     * @param out      输出流
     * @author Administrator
     */
    @SuppressWarnings("unchecked")
    public void exportExcel(String title, Collection<T> dataset, OutputStream out) {
        // 声明一个工作薄
        try {
            //首先检查数据看是否是正确的
            Iterator<T> iterator = dataset.iterator();
            if (!iterator.hasNext() || title == null || out == null) {
                throw new Exception("传入的数据不对！");
            }
            //取得实际泛型类
            T tObject = iterator.next();
            Class<T> clazz = (Class<T>) tObject.getClass();

            SXSSFWorkbook workbook = new SXSSFWorkbook();
            // 生成一个表格
            SXSSFSheet sheet = workbook.createSheet(title);
            // 设置表格默认列宽度为20个字节
            sheet.setDefaultColumnWidth(20);
            // 生成一个样式

            //HSSFCellStyle style = workbook.createCellStyle();
            // 设置标题样式
            //style = ExcelStyle.setHeadStyle(workbook, style);
            // 得到所有字段
            Field[] filed = tObject.getClass().getDeclaredFields();

            // 标题
            List<String> exportfieldtile = new ArrayList<>();
            // 导出的字段的get方法
            List<Method> methodObj = new ArrayList<>();
            // 遍历整个filed
			for (Field field : filed) {
				ExcelAnnotation excelAnnotation = field.getAnnotation(ExcelAnnotation.class);
				// 如果设置了annotation
				if (excelAnnotation != null) {
					String export = excelAnnotation.value();
					// 添加到标题
					exportfieldtile.add(export);
					// 添加到需要导出的字段的方法
					String fieldName = field.getName();
					String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
					Method getMethod = clazz.getMethod(getMethodName);
					methodObj.add(getMethod);
				}
			}

            // 产生表格标题行
            SXSSFRow row = sheet.createRow(0);
            for (int i = 0; i < exportfieldtile.size(); i++) {
                SXSSFCell cell = row.createCell(i);
                XSSFRichTextString text = new XSSFRichTextString(exportfieldtile.get(i));
                cell.setCellValue(text);
            }

            // 循环整个集合
            int index = -1;
            int num = 0;
            iterator = dataset.iterator();
            while (iterator.hasNext()) {
                //从第二行开始写，第一行是标题
                index++;
                if ((index + 1) % 60000 == 0) {
                    sheet = workbook.createSheet(title + num);
                    T t = iterator.next();
                    for (int k = 0; k < methodObj.size(); k++) {
                        SXSSFCell cell = row.createCell(k);
                        Method getMethod = methodObj.get(k);
                        Object value = getMethod.invoke(t);
                        String textValue = getValue(value);
                        cell.setCellValue(textValue);
                    }
                    num++;

                }
                row = sheet.createRow((index + 1) - (num * 60000));
                T t = iterator.next();
                for (int k = 0; k < methodObj.size(); k++) {
                    SXSSFCell cell = row.createCell(k);
                    Method getMethod = methodObj.get(k);
                    Object value = getMethod.invoke(t);
                    String textValue = getValue(value);
                    cell.setCellValue(textValue);
                }
            }
            workbook.write(out);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public String getValue(Object value) {
        String textValue = "";
        if (value == null) {
            return textValue;
        }

        if (value instanceof Boolean) {
            boolean bValue = (Boolean) value;
            textValue = "是";
            if (!bValue) {
                textValue = "否";
            }
        } else if (value instanceof Date) {
            Date date = (Date) value;
            textValue = sdf.format(date);
        } else {
            textValue = value.toString();
        }

        return textValue;
    }

    /**
     * 初始化导出的excel标题的样式
     */
    public static HSSFCellStyle setTitleStyle(HSSFWorkbook workbook,
                                              HSSFCellStyle style) {

        style.setFillForegroundColor(HSSFColor.WHITE.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style.setBorderTop(HSSFCellStyle.BORDER_THIN);
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        // 生成字体
        HSSFFont font = workbook.createFont();
        font.setColor(HSSFColor.BLACK.index);
        font.setFontHeightInPoints((short) 16);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        // 把字体应用到当前的样样式
        style.setFont(font);
        return style;

    }

    /**
     * 统计部分的样式
     */
    public static HSSFCellStyle setStatStyle(HSSFWorkbook workbook,
                                             HSSFCellStyle style) {

        style.setFillForegroundColor(HSSFColor.WHITE.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        style.setBorderBottom(HSSFCellStyle.BORDER_NONE);
        style.setBorderLeft(HSSFCellStyle.BORDER_NONE);
        style.setBorderRight(HSSFCellStyle.BORDER_NONE);
        style.setBorderTop(HSSFCellStyle.BORDER_NONE);
        style.setAlignment(HSSFCellStyle.ALIGN_LEFT);
        // 生成字体
        HSSFFont font = workbook.createFont();
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        // 把字体应用到当前的样样式
        style.setFont(font);
        return style;

    }

    /**
     * 初始化导出的excel样式
     */
    public static HSSFCellStyle setHeadStyle(HSSFWorkbook workbook, HSSFCellStyle style) {
        style.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style.setBorderTop(HSSFCellStyle.BORDER_THIN);
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        // 生成字体
        HSSFFont font = workbook.createFont();
        font.setColor(HSSFColor.VIOLET.index);
        font.setFontHeightInPoints((short) 12);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        // 把字体应用到当前的样样式
        style.setFont(font);
        return style;

    }

    public static HSSFCellStyle setbodyStyle(HSSFWorkbook workbook, HSSFCellStyle style2) {
        style2.setFillForegroundColor(HSSFColor.LIGHT_YELLOW.index);
        style2.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        style2.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style2.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style2.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style2.setBorderTop(HSSFCellStyle.BORDER_THIN);
        style2.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        style2.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        // 生成字体
        HSSFFont font2 = workbook.createFont();
        font2.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
        // 把字体应用到当前的样样式  
        style2.setFont(font2);
        return style2;
    }
}
